package com.wanwu.channel.notify.process;


import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.wanwu.channel.common.enums.AlarmTypeEnum;
import com.wanwu.channel.common.enums.ChannelPayStatus;
import com.wanwu.channel.common.enums.ChannelRefundStatus;
import com.wanwu.channel.common.enums.ExceptionEnum;
import com.wanwu.channel.common.exception.SystemException;
import com.wanwu.channel.common.util.AmountUtil;
import com.wanwu.channel.common.util.DateUtils;
import com.wanwu.channel.common.util.LogUtil;
import com.wanwu.channel.core.ChannelPayService;
import com.wanwu.channel.core.enums.AliPayTradeStatusEnum;
import com.wanwu.channel.core.tool.AlarmPoolUtil;
import com.wanwu.channel.dao.entity.ChannelPayEntity;
import com.wanwu.channel.dao.entity.ChannelRefundEntity;
import com.wanwu.channel.notify.config.WeixinConfig;
import com.wanwu.monitor.api.util.vo.AlarmMsgVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 支付宝回调通知
 */
@Component
public class AlipayNotifyProcess {

    private static final Logger logger = LoggerFactory.getLogger(AlipayNotifyProcess.class);

    @Resource
    private ChannelPayService channelPayService;

    @Resource
    private AlarmPoolUtil alarmPoolUtil;



    @Resource
    private BusinessNotifyProcess businessNotifyProcess;

    @Autowired
    private WeixinConfig weixinConfig;


    /**
     * 支付宝统一回调入口
     *
     * 统一收单退款接口暂时不支持设置notify_url异步地址参数来接收异步通知。
     * 未支付关单、退款、支付成功通知
     * @param notifyParam
     * @return
     */
    public void commonNotify(Map<String, String> notifyParam, String bizCode, int thirdFlag) {
        /**
         * 1、部分退款成功，交易状态仍然是交易成功（TRADE_SUCCESS），所以都会触发异步通知；
         * 2、全额退款成功，交易状态变为交易关闭（TRADE_CLOSED），具体是否会触发异步通知根据该交易支付接口中的通知触发条件判断
         * 3、超时关单
         * 4、支付成功
         * 触发回调的状态：TRADE_SUCCESS（交易成功），TRADE_CLOSED（交易关闭），TRADE_FINISHED（交易完成）
         */
        String tradeStatus = notifyParam.get("trade_status");
        //相较支付成功等返回异步通知，退款的异步通知参数中一定会返回out_biz_no（商户业务号）、refund_fee（总退款金额）、gmt_refund（交易退款时间）
        String outBizNo = notifyParam.get("out_biz_no");
        String refundFee = notifyParam.get("refund_fee");
        //相较等待支付超时关闭返回的异步通知, 全额退款导致交易交易关闭的的异步通知参数中一定会返回gmt_payment（交易付款时间）、gmt_refund（交易退款时间）等
        String gmtPayment = notifyParam.get("gmt_payment");
        String gmtRefund = notifyParam.get("gmt_refund");

        String bizOrderNo = notifyParam.get("out_trade_no");
        //状态转化
        AliPayTradeStatusEnum payTradeStatusEnum = AliPayTradeStatusEnum.getTradeStatusEnum(tradeStatus);
        if (payTradeStatusEnum == null || payTradeStatusEnum.equals(AliPayTradeStatusEnum.WAIT_BUYER_PAY)) {
            //报警通知稍后再加
            //String msg = "bizOrderNo：" + bizOrderNo + "\n支付宝回调异常状态状态：" + tradeStatus;
            //DingUtil.sendDingAtPeople(weixinConfig.getDingKey(), "【支付宝】回调状态与本地不一致", msg, null);
            AlarmMsgVo alarmMsgVo = new AlarmMsgVo.Builder().head("渠道系统报警：【支付宝】回调状态与本地不一致")
                    .field("bizOrderNo", bizOrderNo)
                    .field("【支付宝】回调异常状态状态", tradeStatus).build();
            alarmPoolUtil.addGatherAlarm(weixinConfig.getDingKey(), AlarmTypeEnum.ALIPAYNOTIFYSTATUSDIFF, alarmMsgVo);
            throw new SystemException(ExceptionEnum.E20005);
        }
        switch (payTradeStatusEnum) {
            case TRADE_FINISHED:
                /**
                 * 当支付成功处理：商户签约的产品不支持退款功能的前提下，买家付款成功后交易状态直接变更为TRADE_FINISHED；
                 * 或者，商户签约的产品支持退款功能的前提下，交易已经成功并且已经超过可退款期限，交易状态也会变更为TRADE_FINISHED 。
                 */
                payNotify(notifyParam, bizCode, thirdFlag);
                break;
            case TRADE_SUCCESS:
                //支付成功通知，状态成功，退款
                if (!Strings.isNullOrEmpty(outBizNo) && !Strings.isNullOrEmpty(refundFee) && !Strings.isNullOrEmpty(gmtRefund)) {
                    //部分退款
               //     refundNotify(notifyParam, bizCode);
                } else {
                    //支付成功
                    payNotify(notifyParam, bizCode, thirdFlag);
                }
                break;
            case TRADE_CLOSED:
                if (!Strings.isNullOrEmpty(gmtPayment) && !Strings.isNullOrEmpty(gmtRefund)) {
                    //全额退款完成关闭
             //       refundNotify(notifyParam, bizCode);
                } else {
                    //未支付超时，关闭
                    closeNotify(notifyParam, bizCode, thirdFlag);
                }
                break;
            default:
                break;
        }
    }

    /**
     *
     * @param tradeStatus
     * @param bizOrderNo
     * @param channelPayEntity
     * @return
     */
    private boolean checkNotifyData(String tradeStatus, String bizOrderNo, ChannelPayEntity channelPayEntity) {
        //单号校验
        if (ObjectUtils.isEmpty(channelPayEntity)) {
            //报警通知稍后再加
            //DingUtil.sendDingAtPeople(weixinConfig.getDingKey(), "【支付宝统一回调】回调订单不存在,bizOrderNo:", bizOrderNo, null);
            AlarmMsgVo alarmMsgVo = new AlarmMsgVo.Builder().head("【支付宝统一回调】回调订单不存在").field("bizOrderNo", bizOrderNo).build();
            alarmPoolUtil.addGatherAlarm(weixinConfig.getDingKey(), AlarmTypeEnum.ALIPAYCOMMONNOTIFYORDERNOTEXIST, alarmMsgVo);
            throw new SystemException(ExceptionEnum.E10011);
        }

        //如果数据库订单状态是终态，则判断微信通知报文的状态跟数据库状态是否一致，一致则直接返回成功，不一致报警
        if (!ChannelPayStatus.PROCESSING.getValue().equals(channelPayEntity.getPayStatus()) && !ChannelPayStatus.NOTPAY.getValue().equals(channelPayEntity.getPayStatus())) {
            if ((AliPayTradeStatusEnum.checkSuc(tradeStatus) && !ChannelPayStatus.SUCCESS.getValue().equals(channelPayEntity.getPayStatus())) ||
                    (AliPayTradeStatusEnum.checkClosed(tradeStatus) && !ChannelPayStatus.checkClosedAndFailedStatus(channelPayEntity.getPayStatus()))
            ) {
                //报警通知稍后再加
                //String msg = "渠道单号：" + channelPayEntity.getChannelNo() + "\n本地状态：" + channelPayEntity.getPayStatus() + "\n回调状态：" + tradeStatus;
                //DingUtil.sendDingAtPeople(weixinConfig.getDingKey(), "【支付宝统一回调】回调状态与本地不一致", msg, null);
                AlarmMsgVo alarmMsgVo = new AlarmMsgVo.Builder().head("渠道系统告警：【支付宝统一回调】回调状态与本地不一致")
                        .field("渠道单号", channelPayEntity.getChannelNo())
                        .field("本地状态", channelPayEntity.getPayStatus())
                        .field("回调状态", tradeStatus).build();
                alarmPoolUtil.addGatherAlarm(weixinConfig.getDingKey(), AlarmTypeEnum.ALIPAYCOMMONNOTIFYSTATUSDIFF, alarmMsgVo);
                throw new SystemException(ExceptionEnum.E10012);
            } else {
                //无需处理
                return true;
            }
        }

        return false;
    }



    /**
     * 支付回调
     * @param notifyParam
     * @param bizCode
     * @param thirdFlag
     * @return
     */
    public void payNotify(Map<String, String> notifyParam, String bizCode, int thirdFlag) {
        String notifyJsonStr = JSON.toJSONString(notifyParam);
        String tradeStatus = notifyParam.get("trade_status");
        Long totalFee = AmountUtil.yuan2longCent(notifyParam.get("total_amount"));
        String bizOrderNo = notifyParam.get("out_trade_no");
        LogUtil.info(logger, "解析成【支付宝直连】支付回调，bizOrderNo：{0}", bizOrderNo);

        //查询订单是否存在
        ChannelPayEntity channelPayEntity = null;
        if(0 == thirdFlag){
            channelPayEntity = channelPayService.queryByBizOrderNoFMaster(notifyParam.get("out_trade_no"), bizCode);
        }else {
            channelPayEntity = channelPayService.queryByChannelNoFMaster(notifyParam.get("out_trade_no"));
        }
        //验证回调单
        if (checkNotifyData(tradeStatus, bizOrderNo, channelPayEntity)) {
            return;
        }

        //判断通知报文中总金额跟数据库是否一致，不一致报警
        if (totalFee.longValue() != channelPayEntity.getRmbAmount().longValue()) {
            //报警通知稍后再加
            //String msg = "渠道单号：" + channelPayEntity.getChannelNo() + "\n本地支付金额：" + channelPayEntity.getRmbAmount() + "\n回调支付金额：" + totalFee;
            //DingUtil.sendDingAtPeople(weixinConfig.getDingKey(), "【支付宝】支付回调订单金额与本地不一致", msg, null);
            AlarmMsgVo alarmMsgVo = new AlarmMsgVo.Builder().head("渠道系统报警：【支付宝】支付回调订单金额与本地不一致")
                    .field("渠道单号", channelPayEntity.getChannelNo())
                    .field("本地支付金额", channelPayEntity.getRmbAmount())
                    .field("回调支付金额", totalFee).build();
            alarmPoolUtil.addGatherAlarm(weixinConfig.getDingKey(), AlarmTypeEnum.ALIPAYPAYNOTIFYAMOUNTDIFF, alarmMsgVo);
            throw new SystemException(ExceptionEnum.E10013);
        }

        //处理回调结果
        channelPayEntity.setThirdOrderNo(notifyParam.get("trade_no"));
        channelPayEntity.setTradeStatus(tradeStatus);
        channelPayEntity.setNotifyData(notifyJsonStr);
        channelPayEntity.setPrepayReturnData(notifyJsonStr);
        channelPayEntity.setPayStatus(ChannelPayStatus.SUCCESS.getValue());
        channelPayEntity.setErrorCode(ChannelPayStatus.SUCCESS.getDesc());
        channelPayEntity.setErrorMsg(ChannelPayStatus.SUCCESS.getDesc());
        channelPayEntity.setPayTime(DateUtils.formatDate(1, notifyParam.get("gmt_payment")));

        //更新数据库
        ChannelPayEntity dao = channelPayService.queryByBizOrderNoFMaster(channelPayEntity.getBizOrderNo(), channelPayEntity.getBizCode());
        //只有数据库记录处于处理中状态以及从微信查到的状态不为处理中才更新
        if (ChannelPayStatus.PROCESSING.getValue().equals(dao.getPayStatus()) || ChannelPayStatus.NOTPAY.getValue().equals(dao.getPayStatus())) {
            channelPayEntity.setUpdateTime(new Date());
            channelPayEntity.setNotifyNum(channelPayEntity.getNotifyNum() + 1);
            channelPayService.updateByChannelNo(channelPayEntity);
        }

        //回调支付系统
        businessNotifyProcess.notifyToBusinessLine(channelPayEntity);
    }


    /**
     * 支付回调
     * @param notifyParam
     * @return
     */
    public void closeNotify(Map<String, String> notifyParam, String bizCode, int thirdFlag) {
        String notifyJsonStr = JSON.toJSONString(notifyParam);
        String tradeStatus = notifyParam.get("trade_status");
        String bizOrderNo = notifyParam.get("out_trade_no");
        LogUtil.info(logger, "解析成【支付宝直连】关单回调，bizOrderNo：{0}", bizOrderNo);
        //查询订单是否存在
        ChannelPayEntity channelPayEntity = null;
        if(0 == thirdFlag){
            channelPayEntity = channelPayService.queryByBizOrderNoFMaster(bizOrderNo, bizCode);
        }else{
            channelPayEntity = channelPayService.queryByChannelNoFMaster(bizOrderNo);
        }
        //验证回调单
        if (checkNotifyData(tradeStatus, bizOrderNo, channelPayEntity)) {
            return;
        }

        //处理回调结果
        channelPayEntity.setThirdOrderNo(notifyParam.get("trade_no"));
        channelPayEntity.setTradeStatus(tradeStatus);
        channelPayEntity.setNotifyData(notifyJsonStr);
        channelPayEntity.setPrepayReturnData(notifyJsonStr);
        channelPayEntity.setPayStatus(ChannelPayStatus.CLOSED.getValue());
        channelPayEntity.setErrorCode(ChannelPayStatus.CLOSED.getDesc());
        channelPayEntity.setErrorMsg(ChannelPayStatus.CLOSED.getDesc());

        //更新数据库
        ChannelPayEntity dao = channelPayService.queryByBizOrderNoFMaster(channelPayEntity.getBizOrderNo(), channelPayEntity.getBizCode());
        //只有数据库记录处于处理中状态以及从微信查到的状态不为处理中才更新
        if (ChannelPayStatus.PROCESSING.getValue().equals(dao.getPayStatus()) || ChannelPayStatus.NOTPAY.getValue().equals(dao.getPayStatus())) {
            channelPayEntity.setUpdateTime(new Date());
            channelPayEntity.setNotifyNum(channelPayEntity.getNotifyNum() + 1);
            channelPayService.updateByChannelNo(channelPayEntity);
        }
        //回调支付系统
        businessNotifyProcess.notifyToBusinessLine(channelPayEntity);
    }


}
